// ============================== 导入依赖 ============================== //

import Vue from 'vue'
import Vuex, { mapState, mapGetters, mapMutations, mapActions } from 'vuex'
import IS from './is'

// ============================== 导出混入工具 ============================== //

export default {
  /**
   * 混入仓库
   *
   * @param {Object} store 仓库对象
   *
   * @returns {Object} { store, computed, methods, watch, created }
   */
  mixStore(store) {
    return getMixStore(store, this) // this = CORE
  },

  /**
   * 混入
   *
   * @param {Object} obj    模块对象 (示例：{ state: [], getters: [], mutations: [] } 或 CORE.STORE.WEB 等)
   * @param {String} path   模块路径 (示例：'' 或 major 或 major/minor，为 '' 可省略)
   * @param {String} prefix 变量前缀 (示例：'' 或 major_ 或 major_minor_，为 '' 可省略)
   *
   * @returns {Object} { computed, methods }
   */
  mix(obj, path = '', prefix = '') {
    return getMixObject(obj, path, prefix)
  },

  /**
   * 混入 WEB 模块 (同上)
   */
  mixWeb(path = '', prefix = '') {
    return getMixObject(filterStore('WEB', this), path, prefix)
  },

  /**
   * 混入 APP 模块 (同上)
   */
  mixApp(path = '', prefix = '') {
    return getMixObject(filterStore('APP', this), path, prefix)
  },

  /**
   * 混入 SCREEN 模块 (同上)
   */
  mixScreen(path = '', prefix = '') {
    return getMixObject(filterStore('SCREEN', this), path, prefix)
  },

  /**
   * 混入 BASE 模块 (同上)
   */
  mixBase(path = '', prefix = '') {
    return getMixObject(filterStore('BASE', this), path, prefix)
  },

  /**
   * 混入 LIST 模块 (同上)
   */
  mixList(path = '', prefix = '') {
    return getMixObject(filterStore('LIST', this), path, prefix)
  },

  /**
   * 混入 ONE 模块 (同上)
   */
  mixOne(path = '', prefix = '') {
    return getMixObject(filterStore('ONE', this), path, prefix)
  },

  /**
   * 混入 MODAL 模块 (同上)
   */
  mixModal(path = '', prefix = '') {
    return getMixObject(filterStore('MODAL', this), path, prefix)
  },

  /**
   * 混入 SUBMIT 模块 (同上)
   */
  mixSubmit(path = '', prefix = '') {
    return getMixObject(filterStore('SUBMIT', this), path, prefix)
  },

  /**
   * 混入 INFO 模块 (同上)
   */
  mixInfo(path = '', prefix = '') {
    return getMixObject(filterStore('INFO', this), path, prefix)
  },

  /**
   * 混入 SELECT 模块 (同上)
   */
  mixSelect(path = '', prefix = '') {
    return getMixObject(filterStore('SELECT', this), path, prefix)
  },

  /**
   * 混入 TREE 模块 (同上)
   */
  mixTree(path = '', prefix = '') {
    return getMixObject(filterStore('TREE', this), path, prefix)
  },

  /**
   * 混入 LOAD 模块 (同上)
   */
  mixLoad(path = '', prefix = '') {
    return getMixObject(filterStore('LOAD', this), path, prefix)
  },

  /**
   * 混入循环
   */
  mixLoop(period) {
    return getMixLoop(period, this)
  },

  /**
   * 混入动画
   */
  mixAnim(period) {
    return getMixAnim(period, this)
  },

  /**
   * 混入接口菜单
   */
  mixApiMenu(title) {
    return getMixApiMenu(title, this)
  },

  /**
   * 混入单词菜单
   */
  mixWordMenu(trans) {
    return getMixWordMenu(trans, this)
  },

  /**
   * 混入下载菜单
   */
  mixDowmMenu() {
    return getMixDowmMenu(this)
  },
}

// ============================== 内部常量 ============================== //

/**
 * 缓存过滤
 */
const FILTER = {
  WEB: null,
}

// ============================== 内部方法 ============================== //

/**
 * 过滤仓库
 *
 * @param {Object} store 仓库
 * @param {Object} CORE 内核
 *
 * @returns {Object} 新仓库
 */
export const filterStore = (store, CORE) => {
  if (!FILTER[store]) {
    const state = Object.keys(CORE.STORE[store].state).filter((name) => {
      if (/^need/.test(name)) return false
      return true
    })
    const getters = Object.keys(CORE.STORE[store].getters)
    const mutations = Object.keys(CORE.STORE[store].mutations).filter((name) => {
      if (['setRefForm', 'setRefTable'].includes(name)) return false
      if (/^complete/.test(name)) return false
      if (/^stop/.test(name)) return false
      return true
    })
    FILTER[store] = { state, getters, mutations, actions: [] }
  }
  return FILTER[store]
}

/**
 * 转换名称
 *
 * @param {String} name major/minor/name
 *
 * @returns {String} major_minor_name
 */
const trans = (name) => name.replace(/\//g, '_')

/**
 * 子模块是否全部有效 (只能字母开头，只能为字母数字)
 *
 * @param {Object} store 仓库
 * @param {Object} CORE 内核
 * @param {String} path 模块路径
 *
 * @returns {Boolean} 是否有效
 */
const validModule = (store, CORE, path = '') => {
  let valid = true
  if (IS.isJSON(store.modules)) {
    for (const item of Object.keys(store.modules)) {
      const itemValid = /^[a-z][A-Za-z0-9]*$/.test(item)
      if (!itemValid) {
        const errMsg = /^[a-z]/.test(item) ? '为字母数字' : '小写字母开头'
        CORE.logError(`检查子模块 ${path}${item}`, store.modules, `模块名 ${item} 只能${errMsg}`)
      }
      valid = valid && itemValid
      valid = validModule(store.modules[item], CORE, `${path}${item} / `) && valid
    }
  }
  if (!path && !valid) CORE.MSG.error('页面初始化：失败')

  return valid
}

/**
 * 获取名称数组 (驼峰命名 / 大写字母下划线)
 *
 * @param {Object} store 仓库
 * @param {String} type 类型
 * @param {Set} set 名称集合
 * @param {String} path 模块路径
 *
 * @returns {Array} 名称数组
 */
const getNames = (store, type, set = new Set(), path = '') => {
  const obj = IS.isFunction(store[type]) ? store[type]() : store[type] || {}

  Object.keys(obj).forEach((item) => {
    const valid = /^[a-z0-9]+([A-Z][a-z0-9]+)*$/.test(item) || /^[A-Z]+(_[A-Z]+)*$/.test(item)
    if (valid) set.add(`${path}${item}`)
  })

  // 递归子模块
  if (IS.isJSON(store.modules)) {
    Object.keys(store.modules).forEach((item) => {
      getNames(store.modules[item], type, set, `${path}${item}/`)
    })
  }
  if (!path) return [...set]
}

/**
 * 获取状态映射对象
 *
 * @param {Array}  names  名称数组
 * @param {String} prefix 变量前缀 (示例：'' 或 major_ 或 major_minor_，为 undefined 则从 name 中转换)
 *
 * @returns {Object} { name: (state) => state.name, major_minor_name: (state) => state.major.minor.name }
 */
const getMapStateObj = (names, prefix) => {
  const obj = {}
  names.forEach((name) => {
    const pathList = name.split('/')
    const last = pathList.pop()
    const prop = IS.isString(prefix) ? `${prefix}${last}` : trans(name)
    obj[prop] = (state) => pathList.reduce((next, item) => next[item], state)[last]
  })
  return obj
}

/**
 * 获取映射对象
 *
 * @param {Array}  names  名称数组
 * @param {String} prefix 变量前缀 (示例：'' 或 major_ 或 major_minor_，为 undefined 则从 name 中转换)
 *
 * @returns {Object} { name: name, major_minor_name: major/minor/name }
 */
const getMapObj = (names, prefix) => {
  const map = {}
  names.forEach((name) => {
    const pathList = name.split('/')
    const last = pathList.pop()
    const prop = IS.isString(prefix) ? `${prefix}${last}` : trans(name)
    map[prop] = name
  })
  return map
}

/**
 * 获取属性对象
 *
 * @param {Object} store 原始仓库
 *
 * @returns {Object} 属性对象
 */
const getProps = (store) => {
  const state = IS.isFunction(store.state) ? store.state() : store.state
  if (IS.isEmpty(state) || state.MAIN_START !== 'startOne') return {}
  return {
    id: { type: [String, Number], default: '' }, // 主键 ID
    obj: { type: Object, default: () => null }, // 对象数据
  }
}

/**
 * 获取监听对象
 *
 * @param {Array} needNames 开关名称数组
 * @param {Array} gettersNames 计算属性名称数组
 * @param {Object} CORE 内核
 * @param {Object} props 属性对象
 *
 * @returns {Object} 映射对象
 */
const getWatchObj = (needNames, gettersNames, CORE, props) => {
  const watch = {}

  // 监听远程开关
  needNames.forEach((name) => {
    const remote = name.replace('need', 'remote')
    const complete = name.replace('need', 'complete')
    const stop = name.replace('need', 'stop')
    const loop = name.replace(/need\w+$/, 'loop')
    watch[name] = function (need, old) {
      if (!need === !old) return // 忽略真假未变的情况 (避免手动设置 need = 1 切为 need = 2 的情况)
      const noLoading = this[loop] && /need(List|One|Info)$/.test(name) // 大屏有循环，列表/单项/信息无遮罩
      if (!noLoading) need ? CORE.loading() : CORE.unloading()
      need && this[remote]().then(this[complete]).catch(this[stop])
    }
  })

  // 监听初始化完成
  const initedNames = gettersNames.filter((name) => name.endsWith('inited')).map((name) => trans(name))
  initedNames.forEach((name) => {
    const start = name.replace('inited', 'MAIN_START')
    watch[name] = function (inited, old) {
      if (!inited === !old) return // 忽略真假未变的情况 (避免 inited = 1 切为 inited = 2 的情况)
      if (inited && this[start]) {
        const MAIN_START = name.replace('inited', this[start])
        if (MAIN_START.endsWith('startOne')) {
          const one = name.replace('inited', 'one')
          if (!this[one].id) return // 单项 ID 为空时，不打开开关
        }
        IS.isFunction(this[MAIN_START]) && this[MAIN_START]()
      }
    }
  })

  // 监听属性变化
  watch.$props = {
    immediate: true,
    deep: true,
    handler(props) {
      const propList = Object.keys(props)
      const { MAIN_START, INIT_INFO_PARAM, INIT_PARAM, one, inited, $set } = this

      if (MAIN_START === 'startInfo' && INIT_INFO_PARAM) {
        propList.forEach((prop) => $set(INIT_INFO_PARAM, prop, this[prop]))
        this.setInfoParam()
        if (inited) this.startInfo()
      }

      if (MAIN_START === 'startList' && INIT_PARAM) {
        propList.forEach((prop) => $set(INIT_PARAM, prop, this[prop]))
        this.setParam()
        if (inited) this.startList(1)
      }

      if (MAIN_START === 'startOne' && one) {
        const otherProp = propList.filter((prop) => ['id', 'obj'].includes(prop))
        if (CORE.isJSON(props.obj)) this.setOne(props.obj) // 直接传入了一个对象
        else if (props.id === one.id) otherProp.forEach((prop) => $set(one, prop, this[prop])) // 只修改属性中的数据
        else this.setOne(props) // 重置所有数据
      }
    },
  }

  // 监听详情页 ID 变化
  if (props.id && needNames.includes('needOne')) {
    watch['one.id'] = function (id) {
      id && this.MAIN_START === 'startOne' && this.startOne() // 有 ID 则重新查询单项
    }
  }

  return watch
}

/**
 * 获取挂载之前方法
 *
 * @param {Array} needNames 开关名称数组
 * @param {Array} mapNames 映射名称数组
 * @param {Object} CORE 内核
 *
 * @returns {Function} 挂载之前
 */
const getBeforeMount = (needNames, mapNames, CORE) => {
  // 筛选开关数组
  const AUTO_NEED = ['needBase', 'needSelect', 'needTree']
  const autoNeeds = needNames.filter((name) => AUTO_NEED.some((item) => name.includes(item)))

  return function () {
    // 重置所有提示
    mapNames.forEach((mapName) => {
      const pre = mapName.slice(0, -3).replace(/\//g, '_')
      const MAP = this[`${pre}MAP`]

      if (!IS.isJSON(MAP)) return

      // 设置提示语
      const can = (Name, Suf = '') => this[`${pre}need${Name}`] !== undefined &&
        (MAP.tipForceWord || !MAP[`tip${Name}${Suf}`]) // 能否设置
      const get = (NAME) => CORE.isString(MAP[NAME]) ? MAP[NAME] : CORE.MAP[`TIP_${NAME}`] // 获取配置

      // 获取类提示
      const GET_LIST = ['Info', 'List', 'One', 'Select', 'Tree']
      GET_LIST.forEach((item) => {
        if (can(item)) MAP[`tip${item}`] = `${get('GET')}${MAP.WORD}${get(item.toUpperCase())}`
      })

      // 提交类提示
      const SUBMIT_LIST = ['Submit', 'Add', 'Edit', 'Del', 'Pass', 'Rej']
      SUBMIT_LIST.forEach((item) => {
        if (can(item)) MAP[`tip${item}`] = `${get(item.toUpperCase())}${MAP.WORD}`
      })
      if (this[`${pre}needSubmit`] !== undefined && !MAP.tipShow) { // 由于没有 needShow，故单独设置
        MAP.tipShow = `${get('SHOW')}${MAP.WORD}`
      }

      // 确认类内容提示
      const CONTENT_LIST = ['Del', 'Pass', 'Rej']
      CONTENT_LIST.forEach((item) => {
        const arr = [get('PREFIX'), get(item.toUpperCase()), get('MIDDLE'), MAP.WORD || '数据', get('SUFFIX')]
        if (can(item, 'Content')) MAP[`tip${item}Content`] = `${arr[0]}${arr[1]}${arr[2]}${arr[3]}${arr[4]}`
      })

      // 上传下载提示
      if (this[`${pre}needUpload`] !== undefined && !MAP.tipUp) {
        MAP.tipUp = `${get('UP')}${MAP.WORD}`
      }
      if (this[`${pre}needDownload`] !== undefined && !MAP.tipDown) {
        MAP.tipDown = `${get('DOWN')}${MAP.WORD}${get('LIST')}`
      }
    })

    // 打开所有自动开关
    autoNeeds.forEach((name) => this[name.replace('need', 'start')]())
  }
}

/**
 * 获取重置开关和就绪的方法
 *
 * @param {Array} needNames 开关名称数组
 * @param {Object} CORE 内核
 *
 * @returns {Function} 重置方法
 */
const getResetNeedReady = (needNames, CORE) => {
  return function (state) {
    needNames.forEach((needName) => {
      const pathList = needName.split('_')
      const name = pathList.pop()
      const module = pathList.reduce((next, item) => next[item] || { INIT_CONFIG: {} }, state)

      module[name.replace('need', 'ready')] = false // 关闭就绪

      if (module[name]) {
        module[name] = false // 关闭进行中的开关
        CORE.unloading() // 关闭遮罩
      }

      if (name === 'needOne') {
        module.one = CORE.merge(module.one || {}, module.INIT_ONE) // 重置单项
      }

      if (name === 'needInfo' && !module.INIT_CONFIG.DESTROY_KEEP_INFO_PARAM) {
        module.info = CORE.merge(module.info || {}, module.INIT_INFO) // 重置信息
        module.infoParam = CORE.merge(module.infoParam || {}, module.INIT_INFO_PARAM) // 重置信息参数
      }

      if (name === 'needList' && !module.INIT_CONFIG.DESTROY_KEEP_PARAM) {
        module.total = 0 // 重置总数
        module.list = [] // 重置列表
        module.param = CORE.merge(module.param || {}, module.INIT_PARAM) // 重置参数
      }

      if (name === 'needSelect') {
        this.commit(needName.replace('needSelect', 'setChosen')) // 重置选中
      }
    })
  }
}

/**
 * 获取混入对象
 *
 * @param {Object} store  模块对象 (示例：{ state: [], getters: [], mutations: [] } 或 CORE.STORE.WEB 等)
 * @param {String} path   模块路径 (示例：'' 或 major 或 major/minor，为 '' 可省略)
 * @param {String} prefix 变量前缀 (示例：'' 或 major_ 或 major_minor_，为 '' 可省略)
 *
 * @returns {Object} { computed: {}, methods: {} }
 */
export const getMixObject = (store, path, prefix) => {
  // 获取名称数组
  const stateNames = IS.isArray(store.state) ? store.state : getNames(store, 'state')
  const gettersNames = IS.isArray(store.getters) ? store.getters : getNames(store, 'getters')
  const mutationsNames = IS.isArray(store.mutations) ? store.mutations : getNames(store, 'mutations')
  const actionsNames = IS.isArray(store.actions) ? store.actions : getNames(store, 'actions')

  const transNames = (names) => names.map((name) => `${path}${path && '/'}${name}`)

  // 返回结果
  return {
    computed: {
      ...mapState(getMapStateObj(transNames(stateNames), prefix)),
      ...mapGetters(getMapObj(transNames(gettersNames), prefix)),
    },
    methods: {
      ...mapMutations(getMapObj(transNames(mutationsNames), prefix)),
      ...mapActions(getMapObj(transNames(actionsNames), prefix)),
    },
  }
}

/**
 * 获取仓库实例
 *
 * @param {Object} CORE 内核
 * @param {Object} store 仓库
 * @param {Array} mapNames 映射名称数组
 */
const getNewStore = (CORE, store, mapNames = []) => {
  const newStore = new Vuex.Store(store)

  mapNames.forEach((mapName) => {
    const pathList = mapName.split('/')
    const name = pathList.pop()
    const state = pathList.reduce((next, item) => next[item], newStore.state)
    const MAP = state[name]

    if (!IS.isJSON(MAP)) return

    // 设置内核缓存
    if (MAP.CACHE && !CORE.CACHE[MAP.CACHE]) {
      Vue.set(CORE.CACHE, MAP.CACHE, {})
    }

    // 设置初始值
    if (IS.isJSON(state.info)) { // 信息
      newStore.commit(mapName.replace(/MAP$/, 'setInfoParam'), state.INIT_INFO_PARAM)
      newStore.commit(mapName.replace(/MAP$/, 'setInfo'), state.INIT_INFO)
    }
    if (IS.isJSON(state.param)) { // 列表
      newStore.commit(mapName.replace(/MAP$/, 'setParam'), state.INIT_PARAM)
    }
    if (IS.isJSON(state.one)) { // 单项
      newStore.commit(mapName.replace(/MAP$/, 'setOne'), state.INIT_ONE)
    }
  })

  return newStore
}

/**
 * 获取混入仓库
 *
 * @param {Object} store 仓库
 * @param {Object} CORE 内核
 *
 * @returns {Object} { store, computed, methods, watch, created }
 */
const getMixStore = (store, CORE) => {
  // 根仓库无须命名空间
  delete store.namespaced

  // 检查子模块有效
  const valid = validModule(store, CORE)
  if (!valid) return { store: getNewStore(CORE, store) }

  // 获取名称数组
  const stateNames = getNames(store, 'state')
  const gettersNames = getNames(store, 'getters')
  const mutationsNames = getNames(store, 'mutations')
  const actionsNames = getNames(store, 'actions')
  const needNames = stateNames.filter((name) => /need[A-Z]\w+$/.test(name)).map((name) => trans(name))
  const mapNames = stateNames.filter((name) => /(^|\/)MAP$/.test(name))

  // 添加重置方法
  store.mutations.resetNeedReady = getResetNeedReady(needNames, CORE)

  const props = getProps(store)

  // 返回结果
  return {
    store: getNewStore(CORE, store, mapNames),
    props,
    computed: { ...mapState(getMapStateObj(stateNames)), ...mapGetters(getMapObj(gettersNames)) },
    methods: { ...mapMutations(getMapObj(mutationsNames)), ...mapActions(getMapObj(actionsNames)) },
    watch: getWatchObj(needNames, gettersNames, CORE, props),
    beforeMount: getBeforeMount(needNames, mapNames, CORE),
    beforeDestroy: function () {
      this.$store.commit('resetNeedReady')
    },
  }
}

/**
 * 获取混入循环
 *
 * @param {Number} period 周期 (单位秒)
 * @param {Object} CORE 内核
 *
 * @returns {Object} { computed, methods, watch }
 */
const getMixLoop = (period, CORE) => {
  return {
    // 开关和周期 (本组件若无，则查找父组件)
    computed: {
      parentLoop() {
        const { loop } = CORE.STORE.SCREEN.state
        let parent = this
        while (parent) {
          if (IS.isBoolean(parent.loop)) return parent.loop
          parent = parent.$parent
        }
        return loop
      },
      parentLoopPeriod() {
        const { LOOP_PERIOD } = CORE.STORE.SCREEN.state
        let parent = this
        while (parent) {
          if (IS.isNumber(parent.LOOP_PERIOD)) return parent.LOOP_PERIOD || LOOP_PERIOD
          parent = parent.$parent
        }
        return LOOP_PERIOD
      },
    },
    watch: {
      'CORE.TIME.second'(second) {
        if (!this.parentLoop) return
        const myPeriod = period || this.parentLoopPeriod
        if (second % myPeriod === 0) this.toLoop(second, myPeriod)
      },
    },
    methods: {
      /**
       * 按秒循环 (可覆盖)
       */
      toLoop(second, period) {
        const { MAIN_START } = this
        IS.isFunction(this[MAIN_START]) && this[MAIN_START]()
      },
    },
  }
}

/**
 * 获取混入动画
 *
 * @param {Number} period 周期 (单位秒)
 * @param {Object} CORE 内核
 *
 * @returns {Object} { computed, methods, watch }
 */
const getMixAnim = (period, CORE) => {
  return {
    // 开关和周期 (本组件若无，则查找父组件)
    computed: {
      parentAnim() {
        const { anim } = CORE.STORE.SCREEN.state
        let parent = this
        while (parent) {
          if (IS.isBoolean(parent.anim)) return parent.anim
          parent = parent.$parent
        }
        return anim
      },
      parentAnimPeriod() {
        const { ANIM_PERIOD } = CORE.STORE.SCREEN.state
        let parent = this
        while (parent) {
          if (IS.isNumber(parent.LOOP_PERIOD)) return parent.LOOP_PERIOD || ANIM_PERIOD
          parent = parent.$parent
        }
        return ANIM_PERIOD
      },
    },
    watch: {
      'CORE.TIME.second'(second) {
        const { parentAnim, parentAnimPeriod, toAnim } = this
        const canLoop = parentAnim && second % (period || parentAnimPeriod) === 0
        if (canLoop) toAnim() // 立即触发动画事件
      },
    },
    methods: {
      /**
       * 动画事件 (可覆盖)
       */
      toAnim() {
        //
      },
    },
  }
}

/**
 * 获取混入接口菜单
 *
 * @param {String} title 标题 (可空)
 * @param {Object} CORE 内核
 *
 * @returns {Object} { created }
 */
const getMixApiMenu = (title, CORE) => {
  return {
    created() {
      const { state = {} } = this.$store || {}
      if (CORE.isJSON(state.API) && CORE.isEmpty(state.API.MENU)) {
        const { nameList } = CORE.MENU.current
        state.API.MENU = title ? [...nameList, title] : nameList
      }
    },
  }
}

/**
 * 获取单词菜单
 *
 * @param {String} trans 转换方法 (可空)
 * @param {Object} CORE 内核
 *
 * @returns {Object} { created }
 */
const getMixWordMenu = (trans, CORE) => {
  return {
    created() {
      const { state = {} } = this.$store || {}
      if (CORE.isJSON(state.MAP)) {
        const { name } = CORE.MENU.current
        state.MAP.WORD = CORE.isFunction(trans) ? trans(name) : name
      }
    },
  }
}

/**
 * 获取混入下载菜单
 *
 * @param {Object} CORE 内核
 *
 * @returns {Object} { created }
 */
const getMixDowmMenu = (CORE) => {
  return {
    created() {
      const { state = {} } = this.$store || {}
      if (CORE.isJSON(state.downParam)) {
        const { nameList } = CORE.MENU.current
        state.downParam.fileName = nameList.join('_')
      }
    },
  }
}
